/*
 * Copyright (C) 2016-2019 ActionTech.
 * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
 */

package com.actiontech.dble.performance;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;

/**
 * genarate random test data
 * such as
 * values ('$date{yyyyMMddHHmmsss-[2014-2015]y}/psn$date{yyyy}s/$int(0-9999)/16767:20725','$char(2,0-99) OPP_$enum(BJ,SH,GZ,SZ)_$int(0-9)',$int(10,11),$int(400,420,500,600,800),$int(0-1000),$int(0-100),Sint(0-10),$int(0-99),'201408040028317067b41c0db-4a93-4360-9eb4-e159d1dbef45',$phone,2,2014071715,2315998,1397,152317998,1395,'0000');
 *
 * @author wuzhih
 */

/**
 * @author wuzhih
 */
public class RandomDataValueUtil {

    /**
     * eval template contains random vars and replace them with real value alues
     * (
     * '${date(yyyyMMddHHmmsss-[2014-2015]y)}/psn${date(yyyy)}s/${int(0-9999)}/1
     * 6 7 6 7 : 2 0 7 2 5 ' , ' $ { s t r i n g ( 2 , 0 - 9 9 )}
     * OPP_${enum(BJ,SH,GZ,SZ)}_${int(0-9)}
     * ',${int(10,11)},$int(400,420,500,600,800),$int(0-1000),$int(0-100),$int(0-10),$int(0-99),'201408040028317067b41c0db-4a93-4360-9eb4-e159d1
     * d b e f 4 5 ' , $ p h o n e , 2 , 2 0 1 4 0 7 1 7 1 5 , 2 3 1 5 9 9 8 , 1
     * 3 9 7 , 1 5 2 3 1 7 9 9 8 , 1 3 9 5 , ' 0 0 0 0 ' )
     *
     * @param templateStr
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static LinkedList<StringItem> parselRandVarTemplateString(
            String templateStr) throws Exception {
        char[] chars = templateStr.toCharArray();
        LinkedList<StringItem> stringItems = new LinkedList<StringItem>();
        int curPos = 0;
        int prevPattenEndPos = 0;
        while (curPos < chars.length) {
            char c = chars[curPos];
            if (c == '$' && curPos + 1 < chars.length
                    && chars[curPos + 1] == '{') {
                int start = curPos;
                curPos += 2;
                int end = -1;
                if (curPos < chars.length) {
                    for (int i = curPos; i < chars.length; i++) {
                        if (chars[i] == '}') {
                            end = i;
                            // found pattern
                            if (prevPattenEndPos < start) {// some constant
                                // string chars
                                StringItem item = new StringItem();
                                item.initString(templateStr.substring(
                                        prevPattenEndPos, start));
                                stringItems.add(item);
                            }
                            // add variable pattern item
                            stringItems.add(StringItemFactory
                                    .parseVarPattern(templateStr.substring(
                                            curPos, end)));
                            prevPattenEndPos = end + 1;
                            curPos = end + 1;
                            break;
                        }
                    }
                    if (end == -1) {
                        // not found pattern end
                        throw new RuntimeException(
                                "can't find var patten end pos ,start at "
                                        + start);
                    }
                } else {
                    curPos++;
                }

            } else {
                curPos++;
            }
        }
        // add last
        if (prevPattenEndPos < templateStr.length()) {
            StringItem item = new StringItem();
            item.initString(templateStr.substring(prevPattenEndPos,
                    templateStr.length()));
            stringItems.add(item);
        }
        return stringItems;
    }

    public static Properties loadFromPropertyFile(String sqlFile)
            throws IOException {
        java.util.Properties pros = new Properties();
        FileInputStream fin = null;
        fin = new FileInputStream(sqlFile);
        pros.load(fin);
        fin.close();
        return pros;
    }

    public static String evalRandValueString(LinkedList<StringItem> items) {
        StringBuilder sb = new StringBuilder();
        for (StringItem item : items) {
            sb.append(item.getValue());
        }
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        String sqlTemplate = "insert into opp_call (logthread, instanceid,callresult,partner, app_id,api_id,apiversion,format,token , phone,calltype, calldate,callminutes,callcost,ecipcalltime,ecipcallcost ,respcode) values ('${date(yyyyMMddHHmmssSSS-[2014-2015]y)}/psn2002s/${int(0-9999)}/${int(1111-9999)}:20725','${char([0-9]2:2)} OPP_${enum(BJ,SH,WU,GZ)}_1',10,${int(10-999)},${int(10-99)},100,3,15,'${date(yyyyMMddHHmmssSSS-[2014-2015]y}${char([a-f,0-9]8:8)}-${char([a-f,0-9]4:4)}-${char([0-9]4:4)}-9eb4-${char([a-f,0-9]12:12)}',${phone(139-189)},2,${date(yyyyMMddHH-[2014-2015]y},2315998,1397,${date(HHmmssSSS)},${int(100-1000)},'${enum(0000,0001,0002)}');";
        System.out.println("SQL template:\r\n" + sqlTemplate);
        LinkedList<StringItem> allItems = parselRandVarTemplateString(sqlTemplate);
        // for (StringItem item : allItems) {
        // System.out.println(item);
        // }
        System.out.println("Random SQLs ");
        int total = 5;
        for (int i = 0; i < total; i++) {
            System.out.println(evalRandValueString(allItems));
        }
    }
}

class StringItemFactory {
    private static final Map<String, Class<? extends StringItem>> strItemsMap = new HashMap<String, Class<? extends StringItem>>();

    static {
        strItemsMap.put("date", DateVarItem.class);
        strItemsMap.put("int", IntVarItem.class);
        strItemsMap.put("char", CharVarItem.class);
        strItemsMap.put("enum", EnumVarItem.class);
        strItemsMap.put("phone", PhoneVarItem.class);

    }

    public static StringItem parseVarPattern(String content)
            throws InstantiationException, IllegalAccessException {
        String name = content.substring(0, content.indexOf('('));
        Class<? extends StringItem> cls = strItemsMap.get(name);
        if (cls == null) {
            throw new RuntimeException("not find var type of  " + name);
        }
        StringItem obj = cls.newInstance();
        obj.initString(content);
        return obj;

    }

}

class PhoneVarItem extends StringItem {

    //
    long[] rang = {13900000000L, 19900000000L};

    public void initString(String content) {
        int start = content.indexOf('(');
        int end = content.indexOf(')');
        String range = content.substring(start + 1, end);
        String[] items = range.split("-");
        rang[0] = Long.parseLong(patchLenth(items[0], 11 - items[0].length()));
        rang[1] = Long.parseLong(patchLenth(items[1], 11 - items[1].length()));
    }

    public static String patchLenth(String origin, int patchlen) {
        StringBuffer sb = new StringBuffer();
        sb.append(origin);
        for (int i = 0; i < patchlen; i++) {
            sb.append('0');
        }
        return sb.toString();
    }

    public String getValue() {
        long span = rang[1] - rang[0] + 1;
        return Math.abs(rand.nextInt()) % span + rang[0] + "";
    }

    @Override
    public String toString() {
        return "PhoneVarItem [rang=" + Arrays.toString(rang) + "]";
    }

}

class EnumVarItem extends StringItem {
    // {enum(BJ,SH,GZ,SZ)
    String[] enums = {};

    public void initString(String content) {
        int start = content.indexOf('(');
        int end = content.indexOf(')');
        String range = content.substring(start + 1, end);
        String[] items = range.split(",");
        enums = items;

    }

    public String getValue() {
        return enums[Math.abs(rand.nextInt()) % enums.length];
    }

    @Override
    public String toString() {
        return "EnumarItem [enums=" + Arrays.toString(enums) + "]";
    }

}

class CharVarItem extends StringItem {

    List<char[]> rang = new ArrayList<char[]>();
    int minLen = 1;
    int maxLen = 256;

    public void initString(String content) {

        // char([a-z]1:3)
        int start = content.indexOf('[');
        int end = content.indexOf(']');
        String[] items = content.substring(start + 1, end).split(",");
        for (String itemStr : items) {
            if (itemStr.indexOf('-') > 0) {
                String[] pair = itemStr.split("-");
                char[] curRange = new char[2];
                curRange[0] = pair[0].charAt(0);
                curRange[1] = pair[1].charAt(0);
                rang.add(curRange);
            }
        }
        int splitPos = content.indexOf(':');
        if (splitPos > 0) {
            int splitStart = (end == -1) ? content.indexOf('(') : end;
            int splitEnd = content.indexOf(')');
            String[] pair = content.substring(splitStart + 1, splitEnd).split(
                    ":");
            minLen = Integer.parseInt(pair[0]);
            maxLen = Integer.parseInt(pair[1]);
        }

    }

    public String getValue() {
        int lenth = Math.abs(rand.nextInt()) % (maxLen - minLen + 1) + minLen;
        char[] chars = new char[lenth];
        for (int i = 0; i < chars.length; i++) {
            int randInt = Math.abs(rand.nextInt());
            int choise = randInt % rang.size();
            char[] choisedRange = rang.get(choise);
            char randChar = (char) (randInt
                    % (choisedRange[1] - choisedRange[0] + 1) + choisedRange[0]);
            chars[i] = randChar;

        }
        return new String(chars);
    }

    @Override
    public String toString() {
        return "CharVarItem [rang=" + rang + ", minLen=" + minLen + ", maxLen="
                + maxLen + "]";
    }

}

class StringItem {
    protected static final Random rand = new Random();
    public String content;

    public StringItem() {

    }

    public String getValue() {
        return content;
    }

    public void initString(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "StringItem [content=" + content + "]";
    }
}

class IntVarItem extends StringItem {
    long[] rang = {0, Integer.MAX_VALUE};
    long[] enums = {};
    boolean isEnumInt = false;

    public void initString(String content) {
        int start = content.indexOf('(');
        int end = content.indexOf(')');
        if (content.indexOf('-') > 0) {
            String range = content.substring(start + 1, end);
            String[] items = range.split("-");
            rang[0] = Integer.parseInt(items[0]);
            rang[1] = Integer.parseInt(items[1]);
        } else {
            isEnumInt = true;
            String range = content.substring(start + 1, end);
            String[] items = range.split(",");
            enums = new long[items.length];
            for (int i = 0; i < enums.length; i++) {
                enums[i] = Long.parseLong(items[i]);
            }
        }

    }

    public String getValue() {
        if (isEnumInt) {
            return enums[Math.abs(rand.nextInt()) % enums.length] + "";
        } else {
            long span = rang[1] - rang[0] + 1;
            return Math.abs(rand.nextInt()) % span + rang[0] + "";
        }
    }

    @Override
    public String toString() {
        return "IntVarItem [rang=" + Arrays.toString(rang) + ", enums="
                + Arrays.toString(enums) + ", isEnumInt=" + isEnumInt + "]";
    }
}

class DateVarItem extends StringItem {
    String format;
    int[] yearRang = {1970, 2999};
    int[] monRang = {1, 12};
    int[] dayRang = {1, 31};
    int[] hourRang = {0, 23};
    int[] minuteRang = {0, 59};
    int[] secondRang = {0, 59};
    int[] sssRang = {0, 999};

    public DateVarItem() {

    }

    public void initString(String content) {
        int fmtEndPos = content.indexOf('-');
        if (fmtEndPos == -1) {
            fmtEndPos = content.indexOf(')');
        }
        format = content.substring(5, fmtEndPos);
        int yearP = content.indexOf("]y", fmtEndPos);
        if (yearP > 0) {
            yearRang = getRangeofPattern(content, yearP);
        }

        int monthP = content.indexOf("]M", fmtEndPos);
        if (monthP > 0) {
            monRang = getRangeofPattern(content, monthP);
        }

        int dayP = content.indexOf("]d", fmtEndPos);
        if (dayP > 0) {
            dayRang = getRangeofPattern(content, dayP);
        }
        int hourP = content.indexOf("]H", fmtEndPos);
        if (hourP > 0) {
            hourRang = getRangeofPattern(content, hourP);
        }
        int minuteP = content.indexOf("]m", fmtEndPos);
        if (minuteP > 0) {
            minuteRang = getRangeofPattern(content, minuteP);
        }
        int secondP = content.indexOf("]s", fmtEndPos);
        if (secondP > 0) {
            secondRang = getRangeofPattern(content, secondP);
        }
        int millisS = content.indexOf("]S", fmtEndPos);
        if (millisS > 0) {
            sssRang = getRangeofPattern(content, millisS);
        }
    }

    private static final int[] getRangeofPattern(String theString, int endPos) {
        String subString = theString.substring(0, endPos);
        int start = subString.lastIndexOf('[');
        String range = subString.substring(start + 1, endPos);
        String[] items = range.split("-");
        int[] values = new int[2];
        values[0] = Integer.parseInt(items[0]);
        values[1] = Integer.parseInt(items[1]);
        return values;
    }

    public String getValue() {
        int yearSpan = yearRang[1] - yearRang[0] + 1;
        int year = Math.abs(rand.nextInt()) % yearSpan + yearRang[0];

        int monthSpan = monRang[1] - monRang[0] + 1;
        int month = Math.abs(rand.nextInt()) % monthSpan + monRang[0];

        int daySpan = dayRang[1] - dayRang[0] + 1;
        int day = Math.abs(rand.nextInt()) % daySpan + dayRang[0];

        int hourSpan = hourRang[1] - hourRang[0] + 1;
        int hour = Math.abs(rand.nextInt()) % hourSpan + hourRang[0];

        int minuteSpan = minuteRang[1] - minuteRang[0] + 1;
        int minute = Math.abs(rand.nextInt()) % minuteSpan + minuteRang[0];

        int secondSpan = secondRang[1] - secondRang[0] + 1;
        int second = Math.abs(rand.nextInt()) % secondSpan + secondRang[0];

        int sssSpan = sssRang[1] - sssRang[0] + 1;
        int sss = Math.abs(rand.nextInt()) % sssSpan + sssRang[0];

        java.util.Calendar cl = Calendar.getInstance();
        cl.set(Calendar.YEAR, year);
        cl.set(Calendar.MONTH, month);
        cl.set(Calendar.DATE, day);
        cl.set(Calendar.HOUR_OF_DAY, hour);
        cl.set(Calendar.MINUTE, minute);
        cl.set(Calendar.SECOND, second);
        cl.set(Calendar.MILLISECOND, sss);
        return new java.text.SimpleDateFormat(format).format(cl.getTime());

    }

    @Override
    public String toString() {
        return "DateVarItem [format=" + format + ", yearRang="
                + Arrays.toString(yearRang) + ", monRang="
                + Arrays.toString(monRang) + ", dayRang="
                + Arrays.toString(dayRang) + "]";
    }

}
